import json
import uuid
from dataclasses import dataclass
from operator import itemgetter
from typing import Dict, Any

from dotenv import load_dotenv
# from dotenv import load_dotenv
from flask import request
from injector import inject
from langchain.memory import ConversationBufferWindowMemory
from langchain_community.chat_models.moonshot import MoonshotChat
from langchain_core.memory import BaseMemory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableLambda, RunnableConfig, RunnablePassthrough
from langsmith.schemas import Run
from internal.core.tools.providers import ProviderFactory
from internal.schema import AppScchema
from internal.service import AppService
from pkg.resp import success_message
from pkg.resp.response import success_json
from pkg.resp.response import validate_error_json
from internal.service.api_tool_service import APIToolService
from internal.task.demoTask import demo


@inject
@dataclass
class AppHandler:
    app_ser: AppService
    providerFactory: ProviderFactory
    api_tool_service: APIToolService

    @classmethod
    def _load_memory_varibles(cls, input: Dict[str, Any], config: RunnableConfig):
        configurable = config.get("configurable", {})
        configurable_memory = configurable.get("memory", None)
        if configurable_memory is not None and isinstance(configurable_memory, BaseMemory):
            print(configurable_memory.load_memory_variables({}))
            return configurable_memory.load_memory_variables(input)
        return {"history": []}

    @classmethod
    def _end_load(cls, input: Run, config: RunnableConfig):
        configurable = config.get("configurable", {})
        configurable_memory = configurable.get("memory", None)
        if configurable_memory is not None and isinstance(configurable_memory, BaseMemory):
            configurable_memory.save_context(input.inputs, input.outputs)

    def chart(self):
        load_dotenv()
        req = AppScchema()
        if not req.validate():
            return validate_error_json(erros=req.errors)
        cover = ConversationBufferWindowMemory(
            return_messages=True,
            k=3,
            # chat_memory=FileChatMessageHistory("./storage/memory/files.txt"),
            input_key="query"
        )
        prompt = ChatPromptTemplate.from_messages([
            ("system", "你是一个强大的聊天机器人，能根据用户的提问回复对应的问题"),
            MessagesPlaceholder("history"),
            ("human", "{query}"),
        ])
        llm = MoonshotChat(model="moonshot-v1-128k")

        # 4.创建链应用
        chain = (RunnablePassthrough.assign(
            history=RunnableLambda(self._load_memory_varibles) | itemgetter("history")
        ) | prompt | llm | StrOutputParser()).with_listeners(on_end=self._end_load)

        # 5.调用链生成内容
        chain_input = {"query": req.query.data}
        content = chain.invoke(chain_input, config={"configurable": {"memory": cover}})
        return success_json(content)

    def addData(self):
        data = json.dumps(request.json.get("data"))
        dd = json.loads(data)
        print("========================")
        print(self)
        self.app_ser.addData(dd)
        return success_json()

    def delData(self, id: uuid.UUID):
        self.app_ser.delData(id)
        return success_json()

    def updateData(self, id: uuid.UUID):
        data = json.dumps(request.json.get("data"))
        dd = json.loads(data)
        self.app_ser.updateData(id, dd)
        return success_json()

    def pings(self):
        demo.delay(uuid.uuid4())
        data = self.api_tool_service.api_tool_invoke()
        return success_json(data)
        # run = self.providerFactory.get_tool_fun("google", "google_serper")
        # val = run.invoke({"query": "2024北京马拉松前三名有那些人"})
        # val = self.providerFactory.get_provider_item_all()
        # print(val)
        # return success_json([item.dict() for item in val])

        # raise FailException("参数未找到")
        # return {"ping": "ping"}
